package com.ylr.basic.framework.service.impl;

import com.ylr.basic.framework.entity.IpAddressEntity;
import com.ylr.basic.framework.feign.external.IpResolveClient;
import com.ylr.basic.framework.mapper.IpAddressMapper;
import com.ylr.basic.framework.service.IpAddressService;
import com.ylr.base.common.constant.RedisConstant;
import com.ylr.base.common.exception.YlrException;
import com.ylr.redis.framework.service.RedisHashService;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylr.redis.framework.service.RedissonService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Supplier;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2021-01-20 21:42:28
 * className: IpAddressServiceImpl ip地址操作请求服务接口实现类
 * version: 1.0
 * description:
 */
@Service("ipAddressService")
@AllArgsConstructor
public class IpAddressServiceImpl extends ServiceImpl<IpAddressMapper, IpAddressEntity> implements IpAddressService {

    private static final Logger log = LoggerFactory.getLogger(IpAddressServiceImpl.class);

    private final IpResolveClient ipResolveClient;

    private final RedisHashService redisHashService;

    private final RedissonService redissonService;

    @Override
    public IpAddressEntity getIpAddress(String ip) {
        if (ip.equals("127.0.0.1")) {
            IpAddressEntity ipAddress = new IpAddressEntity();
            ipAddress.setPro("本地请求");
            return ipAddress;
        }
        // 查询Redis缓存，根据ip地址获取真实地址信息
        IpAddressEntity cacheIpAddress = redisHashService.get(RedisConstant.IP_ADDRESS_MAP, ip);
        if (cacheIpAddress != null) {
            return cacheIpAddress;
        }
        String lockKey = RedisConstant.LOCK_PREFIX + ip;
        Supplier<IpAddressEntity> supplier = () -> {
            // 重复查询Redis缓存，根据ip地址获取真实地址信息
            IpAddressEntity ipAddress = redisHashService.get(RedisConstant.IP_ADDRESS_MAP, ip);
            if (ipAddress != null) {
                return ipAddress;
            }

            LambdaQueryWrapper<IpAddressEntity> ipAddressLambdaWrapper = new LambdaQueryWrapper<>();
            // 匹配 ip地址 字段
            ipAddressLambdaWrapper.eq(IpAddressEntity::getIp, ip);
            // 查询数据库ip地址关联显示地址表，获取ip地址对应的地址信息
            List<IpAddressEntity> ipAddressList = this.list(ipAddressLambdaWrapper);
            if (!ipAddressList.isEmpty()) {
                if (ipAddressList.size() > 1) {
                    log.warn("系统存在ip关联显示地址重复信息，请处理。ip={}", ip);
                }
                ipAddress = ipAddressList.get(0);

                // 操作Redis缓存，增加ip关联的真实地址信息
                redisHashService.put(RedisConstant.IP_ADDRESS_MAP, ip, ipAddress);

                return ipAddress;
            }
            // 请求外部接口，获取ip地址关联的现实地址信息
            String responseBody = ipResolveClient.getAddress(ip, true);
            log.info("请求外部接口获取ip地址关联显示地址信息返回结果：{}", responseBody);
            // 请求结果转化
            ipAddress = JSON.parseObject(responseBody, IpAddressEntity.class);
            if (ipAddress == null) {
                throw new YlrException("请求源来路不明，请确认，如有问题，请联系客服处理。");
            }
            if (StringUtils.isNotBlank(ipAddress.getErr())) {
                if (StringUtils.isBlank(ipAddress.getAddr())) {
                    throw new YlrException("未知请求源，请确认，如有问题，请联系客服处理。");
                }
                ipAddress.setPro(ipAddress.getAddr().trim());
            }

            // 操作数据库ip地址关联现实地址表，添加一条记录
            int count = baseMapper.insert(ipAddress);
            if (count == 0) {
                throw new YlrException("添加ip地址关联显示地址信息失败，请务必及时处理");
            }
            // 操作Redis缓存，添加一条ip地址关联现实地址缓存
            redisHashService.put(RedisConstant.IP_ADDRESS_MAP, ip, ipAddress);

            return ipAddress;
        };
        // 加锁运行
        return redissonService.lockRun(lockKey, supplier);
    }
}